package com.googlecode.httpclientgae

import org.apache.http.conn.ManagedClientConnection
import java.util.concurrent.TimeUnit
import org.apache.http.params.HttpParams
import org.apache.http.protocol.HttpContext
import org.apache.http.conn.routing.HttpRoute
import java.io.IOException
import org.apache.http.message.BasicHttpResponse
import org.apache.http.{ProtocolVersion, HttpResponse, HttpEntityEnclosingRequest, HttpRequest, HttpHost}
import java.net.{HttpURLConnection, URL}
import scala.collection.JavaConversions._
import org.apache.http.entity.InputStreamEntity

class GaeClientConnection(connectionManager: GaeConnectionManager,
                          private var route: HttpRoute,
                          private var state: Object) extends ManagedClientConnection {

  private var reusable = true
  private var closed = false
  private var conn: HttpURLConnection = null

  def abortConnection = {
    unmarkReusable
    shutdown
  }

  def releaseConnection = connectionManager.releaseConnection(this, 0, TimeUnit.MILLISECONDS)

  def flush = if (!closed && conn != null && conn.getDoOutput) conn.getOutputStream.flush

  def receiveResponseEntity(response: HttpResponse) {
    response.setEntity(new InputStreamEntity(conn.getInputStream, conn.getContentLength))
  }

  def receiveResponseHeader = {
    val response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), conn.getResponseCode, null)
    val headerFields = conn.getHeaderFields
    for (headerField <- headerFields.entrySet if headerField.getKey != null; headerValue <- headerField.getValue) {
      response.addHeader(headerField.getKey.toLowerCase, headerValue)
    }
    response
  }

  def sendRequestEntity(request: HttpEntityEnclosingRequest) {
    conn.setDoOutput(true)
    conn.setRequestProperty(request.getEntity.getContentType.getName, request.getEntity.getContentType.getValue)
    conn.setRequestProperty("Content-Length", request.getEntity.getContentLength.toString)
    request.getEntity.writeTo(conn.getOutputStream)
  }

  def sendRequestHeader(request: HttpRequest) {
    val host = route.getTargetHost
    val url = new URL(host.getSchemeName, host.getHostName, host.getPort, request.getRequestLine.getUri)
    conn = url.openConnection.asInstanceOf[HttpURLConnection]
    //TODO write test for not following redirects
    conn.setInstanceFollowRedirects(false)
    conn.setRequestMethod(request.getRequestLine.getMethod)
    for (header <- request.getAllHeaders) conn.setRequestProperty(header.getName, header.getValue)
  }

  def isResponseAvailable(timeout: Int) = false

  def getRemotePort =
    connectionManager.getSchemeRegistry.getScheme(route.getTargetHost).resolvePort(route.getTargetHost.getPort)

  def getRemoteAddress = null
  // return null

  def getLocalPort = 0
  // return 0

  def getLocalAddress = null
  // return null

  def setIdleDuration(duration: Long, unit: TimeUnit) = {}
  // do nothing

  def getState = state

  def setState(state: AnyRef) = {this.state = state}

  def isMarkedReusable = reusable

  def unmarkReusable = {reusable = false}

  def markReusable = {reusable = true}

  def layerProtocol(context: HttpContext, params: HttpParams) = {
    throw new IOException("layerProtocol() not supported")
  }

  def tunnelProxy(next: HttpHost, secure: Boolean, params: HttpParams) = {
    throw new IOException("tunnelProxy() not supported")
  }

  def tunnelTarget(secure: Boolean, params: HttpParams) = {
    throw new IOException("tunnelTarget() not supported")
  }

  def open(route: HttpRoute, context: HttpContext, params: HttpParams) = {
    this.route = route
    closed = false
  }

  //TODO implement isSecure
  def getSSLSession = null
  // return null

  def getRoute = route

  //TODO implement isSecure
  def isSecure = false
  // only return false yet

  def getMetrics = null
  // return null

  //TODO write test
  def shutdown = {
    if (conn != null) {
      if (conn.getDoOutput && conn.getOutputStream != null) try conn.getOutputStream.close catch {case _ =>;}
      if (conn.getDoInput && conn.getInputStream != null) try conn.getInputStream.close catch {case _ =>;}
    }
    conn = null
    closed = true
  }

  def getSocketTimeout = -1
  // return -1

  def setSocketTimeout(timeout: Int) = {}
  // do nothing

  def isStale = false
  // return false

  def isOpen = !closed

  //TODO write test
  def close = if (!closed) {
    flush
    shutdown
  }

}